#include "linux/common.h"
#include "linux/bug.h"
#include "linux/slab.h"
#include "linux/interrupt.h"
#include "linux/printk.h"
#include "linux/string.h"
#include "linux/mm.h"
#include "asm/cache.h"

/*
 * 获取面表
 */
void *kmem_getpages(kmem_cache_t *cachep, unsigned long flags)
{
    void *addr;

    flags |= cachep->gfpflags;
    addr = (void *)__get_free_pages(flags, cachep->gfporder);
    
    return addr;
}

/*
 * 为 slab 管理结构分配内存，并根据配置决定管理结构的位置（在 slab 内部或外部）
 */
static inline slab_t *kmem_cache_slabmgmt(kmem_cache_t *cachep, void *objp, int local_flags)
{
    slab_t *slabp;
    int offset = 0;

    if (OFF_SLAB(cachep))
    {
        /*
         * 对于off_slab的情况，从专门的缓存 cachep->slabp_cache 分配 slab_t 结构和空闲索引数组
         *
         * 在kmem_cache_create()中已经计算好了 slabp_cache:
         * slab_size = L1_CACHE_ALIGN(cachep->num * sizeof(kmem_bufctl_t) + sizeof(slab_t));
         * if (flags & CFLGS_OFF_SLAB)
         *     cachep->slabp_cache = kmem_find_general_cachep(slab_size, 0);
        */
        slabp = kmem_cache_alloc(cachep->slabp_cache, local_flags);
        if (!slabp)
            return NULL;
    }
    else
    {
        /*
        *  对于on_slab的情况, slab_t结构体和空闲索引数组都放在slab内存块的起始位置
        *  s_mem = objp + sizeof(slab_t) + (cachep->num * sizeof(kmem_bufctl_t))
        *          |      |                |
        *          |      |                空闲索引数组总大小
        *          |      slab_t结构体大小
        *          slab内存块起始地址
        */
        slabp = objp; // 管理结构直接嵌入在slab内存开头
        offset = (cachep->num * sizeof(kmem_bufctl_t)) + sizeof(slab_t);
    }

    slabp->inuse = 0; // 初始化已使用对象计数为0
    slabp->s_mem = (void *)((char *)objp + L1_CACHE_ALIGN(offset));

    return slabp;
}

/*
 * 初始化 slab 中的对象管理结构
 */
void kmem_cache_init_objs(kmem_cache_t *cachep, slab_t *slabp, unsigned long ctor_flags)
{
    int i;

    for (i = 0; i < cachep->num; i++)
        slab_bufctl(slabp)[i] = i + 1;
    
    slab_bufctl(slabp)[i - 1] = BUFCTL_END;
    slabp->free = 0;
}

/*
 * 增加缓存的内存池
 * 分配连续的物理页面作为新的 slab
 * 分配 slab 管理结构（slab_t）
 * 初始化 slab 中的对象管理结构
 * 将新 slab 添加到缓存的 slab 列表中
 */
int kmem_cache_grow(kmem_cache_t *cachep, int flags)
{
    slab_t *slabp;
    struct page *page;
    void *objp;
    unsigned int i, local_flags;
    unsigned long ctor_flags;
    unsigned long save_flags;

    /* 
 	 * SLAB_DMA SLAB_LEVEL_MASK SLAB_NO_GROW是被允许的标志，取反表示不被允许的标示, 如果flags里有不被允许的标志则报BUG
     *
     *  缓存创建时设定的标志必须在整个生命周期保持一致
     * kmem_cache_t *cache = kmem_cache_create(name, size, align, SLAB_DMA | SLAB_KERNEL, ...);

     * 后续的所有操作（包括增长）必须保持相同的约束
     * void *obj1 = kmem_cache_alloc(cache, SLAB_KERNEL);  // 可以睡眠
     * void *obj2 = kmem_cache_alloc(cache, SLAB_ATOMIC);  // 错误！标志不匹配
     * kmem_cache_grow 必须使用与创建时一致的标志
     * 
     * 为什么这三个标志是被允许的？
     * SLAB_DMA: 操作需要物理上连续的内存, 某些设备只能访问特定的物理地址范围, 如低16MB, 需要确保分配的内存可以被设备直接访问
     *
     * SLAB_LEVEL_MASK: 表示分配的内存的上下文级别, 允许指定分配内存时是否可以睡眠或阻塞
     * 中断上下文：不能睡眠，必须使用 SLAB_ATOMIC
     * 进程上下文：可以睡眠，使用 SLAB_KERNEL 或 SLAB_USER
     * 
     * SLAB_NO_GROW: 禁止缓存增长, 如果设置了这个标志, kmem_cache_grow 会直接返回, 不会尝试分配新的 slab
     * 需求场景：
     * 实时系统：避免内存分配引起的不确定性
     * 内存受限环境：防止缓存无限制增长
     * 调试目的：测试缓存耗尽的处理逻辑
	 */
    if (flags & ~(SLAB_DMA | SLAB_LEVEL_MASK | SLAB_NO_GROW))
        BUG();
    
    // SLAB_NO_GROW：禁止增长，如果设置则直接返回
    if (flags & SLAB_NO_GROW)
        return 0; 

    // 在中断上下文中分配内存时，必须使用 SLAB_ATOMIC 标志
    if (in_interrupt() && (flags & SLAB_LEVEL_MASK) != SLAB_ATOMIC)
        BUG();

    // 设置构造函数的标志位
    ctor_flags = SLAB_CTOR_CONSTRUCTOR;
    local_flags = (flags & SLAB_LEVEL_MASK);
    if (local_flags == SLAB_ATOMIC)
        ctor_flags |= SLAB_CTOR_ATOMIC;

    /*
     * cachep->growing++：标记缓存正在增长，防止并发问题 
     * DFLGS_GROWN：设置动态标志，表示缓存已经增长过
     */
    spin_lock_irqsave(&cachep->spinlock, save_flags);
    cachep->dflags |= DFLGS_GROWN;
    cachep->growing++;
    spin_unlock_irqrestore(&cachep->spinlock, save_flags);

    // 分配连续的物理页面，大小为 2^(gfporder) 个页面
    if (!(objp = kmem_getpages(cachep, flags)))
        goto failed;

    // 分配slab_t 结构
    if (!(slabp = kmem_cache_slabmgmt(cachep, objp, local_flags)))
        goto opps1;

    i = 1 << cachep->gfporder; // 计算页面数量
    page = virt_to_page(objp); // 虚拟地址转物理页面
    do
    {
        SET_PAGE_CACHE(page, cachep); // 页面关联到缓存
        SET_PAGE_SLAB(page, slabp); // 页面关联到slab
        PageSetSlab(page); // 标记页面属于slab
        page++;
    } while (--i);

    // 初始化 slab 中的对象管理结构
    kmem_cache_init_objs(cachep, slabp, ctor_flags);

    spin_lock_irqsave(&cachep->spinlock, save_flags);
    // 结束增长标记
    cachep->growing--;

    // 将新 slab 添加到缓存的 slab 列表中
    list_add_tail(&slabp->list, &cachep->slabs);
    if (cachep->firstnotfull == &cachep->slabs) //更新 firstnotfull：如果之前没有非满 slab，指向新 slab
        cachep->firstnotfull = &slabp->list;
    STATS_INC_GROWN(cachep); // 增长计数增加
    cachep->failures = 0; // 重置失败计数

    spin_unlock_irqrestore(&cachep->spinlock, save_flags);
    return 1;
opps1:
    kmem_freepages(cachep, objp); // 释放已分配的页面
failed:
    spin_lock_irqsave(&cachep->spinlock, save_flags);
    cachep->growing--;  // 仍然需要清除增长标记
    spin_unlock_irqrestore(&cachep->spinlock, save_flags);
    return 0;
}

/*
 * 从指定的 slab 中分配一个对象
 */
void *kmem_cache_alloc_one_tail(kmem_cache_t *cachep, slab_t *slabp)
{
    void *objp;

    STATS_INC_ALLOCED(cachep);
    STATS_INC_ACTIVE(cachep);
    STATS_SET_HIGH(cachep);

    // 使用计数 +1
    slabp->inuse++;
    objp = slabp->s_mem + slabp->free * cachep->objsize; //获取对象
    slabp->free = slab_bufctl(slabp)[slabp->free]; // 更新空闲索引

    if (slabp->free == BUFCTL_END)
        cachep->firstnotfull = slabp->list.next; // slab满了，更新 firstnotfull 指针

    return objp;
}

#define kmem_cache_alloc_one(cachep)                    \
    ({                                                  \
        slab_t *slabp;                                  \
                                                        \
        {                                               \
            /* 没有空闲内存则申请请的 */                \
            struct list_head *p = cachep->firstnotfull; \
            if (p == &cachep->slabs)                    \
                goto alloc_new_slab;                    \
            slabp = list_entry(p, slab_t, list);        \
        }                                               \
        kmem_cache_alloc_one_tail(cachep, slabp);       \
    })

void kmem_cache_alloc_head(kmem_cache_t *cachep, int flags)
{
}

/*
 * 分配缓存对象
 */
void *__kmem_cache_alloc(kmem_cache_t *cachep, int flags)
{
    unsigned long save_flags;
    void *objp;

    kmem_cache_alloc_head(cachep, flags);
try_again:
    local_irq_save(save_flags);
#ifdef CONFIG_SMP
    {
        cpucache_t *cc = cc_data(cachep); // 获取当前CPU的缓存

        if (cc)
        {
            if (cc->avail) 
            {
                STATS_INC_ALLOCHIT(cachep);
                objp = cc_entry(cc)[--cc->avail]; // 从每CPU缓存分配
            }
            else
            {
                STATS_INC_ALLOCMISS(cachep);
                objp = kmem_cache_alloc_batch(cachep, flags); // 尝试批量分配到每CPU缓存
                if (!objp)
                    goto alloc_new_slab_nolock;
            }
        }
        else
        {
            spin_lock(&cachep->spinlock);
            objp = kmem_cache_alloc_one(cachep);   // 从全局缓存分配
            spin_unlock(&cachep->spinlock);
        }
    }
#else
    objp = kmem_cache_alloc_one(cachep); // 从全局缓存分配
#endif
    local_irq_restore(save_flags);
    return objp;
alloc_new_slab:
#ifdef CONFIG_SMP
    spin_unlock(&cachep->spinlock);
alloc_new_slab_nolock:
#endif
    local_irq_restore(save_flags);
    if (kmem_cache_grow(cachep, flags)) // 增长缓存
        goto try_again;
    return NULL;
}

void *kmem_cache_alloc(kmem_cache_t *cachep, int flags)
{
    return __kmem_cache_alloc(cachep, flags);
}

/*
 * 释放缓存对象
 */
void kmem_cache_free_one(kmem_cache_t *cachep, void *objp)
{

    slab_t *slabp;

    // 获取对象所属的 slab
    slabp = GET_PAGE_SLAB(virt_to_page(objp));

#if DEBUG
    if (cachep->flags & SLAB_DEBUG_INITIAL)
        /* Need to call the slab's constructor so the
		 * caller can perform a verify of its state (debugging).
		 * Called without the cache-lock held.
		 */
        cachep->ctor(objp, cachep, SLAB_CTOR_CONSTRUCTOR | SLAB_CTOR_VERIFY);

    if (cachep->flags & SLAB_RED_ZONE)
    {
        objp -= BYTES_PER_WORD;
        if (xchg((unsigned long *)objp, RED_MAGIC1) != RED_MAGIC2)
            /* Either write before start, or a double free. */
            BUG();
        if (xchg((unsigned long *)(objp + cachep->objsize -
                                   BYTES_PER_WORD),
                 RED_MAGIC1) != RED_MAGIC2)
            /* Either write past end, or a double free. */
            BUG();
    }
    if (cachep->flags & SLAB_POISON)
        kmem_poison_obj(cachep, objp);
    if (kmem_extra_free_checks(cachep, slabp, objp))
        return;
#endif
    {
        unsigned int objnr = (objp - slabp->s_mem) / cachep->objsize; // 计算对象在 slab 中的索引

        slab_bufctl(slabp)[objnr] = slabp->free; // 将当前空闲索引链表头赋值给该对象的 bufctl
        slabp->free = objnr; // 更新空闲链表头为该对象
    }

    if (slabp->inuse-- == cachep->num)
        goto moveslab_partial; // 之前是满的，现在变成部分使用
    if (!slabp->inuse)
        goto moveslab_free; // 之前是部分使用，现在变成空的
    return;

moveslab_partial:
    {
        struct list_head *t = cachep->firstnotfull; // 获取第一个非满 slab

        cachep->firstnotfull = &slabp->list; // 更新 firstnotfull 指向当前 slab
        if (slabp->list.next == t) // 如果当前 slab 已经是第一个非满 slab, 则直接返回
            return;
        list_del(&slabp->list); // 从当前链表中删除 slab
        list_add_tail(&slabp->list, t); // 将 slab 添加到非满链表
        return;
    }
moveslab_free:
    {
        struct list_head *t = cachep->firstnotfull->prev; // 获取非满链表的最后一个 slab

        list_del(&slabp->list); // 从当前链表中删除 slab
        list_add_tail(&slabp->list, &cachep->slabs); // 将 slab 添加到满链表的末尾
        if (cachep->firstnotfull == &slabp->list)
            cachep->firstnotfull = t->next;
        return;
    }
}

/*
 * 释放一块连续的物理页面
 */
void kmem_freepages(kmem_cache_t *cachep, void *addr)
{
    unsigned long i = (1 << cachep->gfporder);
    struct page *page = virt_to_page(addr);

    while (i--)
    {
        PageClearSlab(page);
        page++;
    }

    free_pages((unsigned long)addr, cachep->gfporder);
}

/*
 * 释放缓存对象
 */
void __kmem_cache_free(kmem_cache_t *cachep, void *objp)
{
#ifdef CONFIG_SMP
    cpucache_t *cc = cc_data(cachep);

    CHECK_PAGE(virt_to_page(objp));
    if (cc)
    {
        int batchcount;
        if (cc->avail < cc->limit)
        {
            STATS_INC_FREEHIT(cachep);
            cc_entry(cc)[cc->avail++] = objp;
            return;
        }
        STATS_INC_FREEMISS(cachep);
        batchcount = cachep->batchcount;
        cc->avail -= batchcount;
        free_block(cachep,
                   &cc_entry(cc)[cc->avail], batchcount);
        cc_entry(cc)[cc->avail++] = objp;
        return;
    }
    else
    {
        free_block(cachep, &objp, 1);
    }
#else
    kmem_cache_free_one(cachep, objp);
#endif
}


void kmem_cache_free(kmem_cache_t *cachep, void *objp)
{
    unsigned long flags;

    local_irq_save(flags);
    __kmem_cache_free(cachep, objp);
    local_irq_restore(flags);
}

#define BREAK_GFP_ORDER_HI 2
#define BREAK_GFP_ORDER_LO 1
static int slab_break_gfp_order = BREAK_GFP_ORDER_LO;
static unsigned long offslab_limit;
static kmem_cache_t cache_cache;

#define cache_chain (cache_cache.next)

/*
 * 估算给定对象大小和标志下的 slab 配置
 * 计算每个 slab 可以容纳的对象数量和剩余空间
 */
void kmem_cache_estimate(unsigned long gfporder, unsigned int size, int flags, unsigned int *left_over, unsigned int *num)
{
    int i;
    unsigned int wastage = PAGE_SIZE << gfporder;
    unsigned int extra = 0;
    unsigned int base = 0;

    // 如果是ON-SLAB, 需要考虑 slab_t 和 kmem_bufctl_t 的空间
    if (!(flags & CFLGS_OFF_SLAB))
    {
        base = sizeof(slab_t);
        extra = sizeof(kmem_bufctl_t);
    }

    // 计算能容纳的对象数量
    i = 0;
    while (i * size + L1_CACHE_ALIGN(base + i * extra) <= wastage) 
        i++;
    if (i > 0)
        i--;

    if (i > SLAB_LIMIT)
        i = SLAB_LIMIT;

    *num = i;
    wastage -= i * size; // 计算剩余空间
    wastage -= L1_CACHE_ALIGN(base + i * extra); // 减去管理结构空间
    *left_over = wastage; // 返回剩余空间(内部碎片)
}

typedef struct cache_sizes
{
    unsigned int cs_size;
    kmem_cache_t *cs_cachep;
    kmem_cache_t *cs_dmacachep;
} cache_sizes_t;

static cache_sizes_t cache_sizes[] =
{
    {32, NULL, NULL},
    {64, NULL, NULL},
    {128, NULL, NULL},
    {256, NULL, NULL},
    {512, NULL, NULL},
    {1024, NULL, NULL},
    {2048, NULL, NULL},
    {4096, NULL, NULL},
    {8192, NULL, NULL},
    {16384, NULL, NULL},
    {32768, NULL, NULL},
    {65536, NULL, NULL},
    {131072, NULL, NULL},
    {0, NULL, NULL}
};

kmem_cache_t *kmem_find_general_cachep(unsigned size, int gfpflags)
{
    cache_sizes_t *csizep = cache_sizes;

    for (; csizep->cs_size; csizep++)
    {
        if (size > csizep->cs_size)
            continue;
        break;
    }
    return (gfpflags & GFP_DMA) ? csizep->cs_dmacachep : csizep->cs_cachep;
}

/*
 * 负责创建新的 SLAB 缓存
 * name：缓存名称，用于标识和调试
 * size：每个对象的大小
 * offset：对象内的偏移量（通常为0）
 * flags：创建标志，控制缓存行为
 * ctor：对象构造函数
 * dtor：对象析构函数（实际很少使用）
 */
kmem_cache_t *kmem_cache_create(char *name, unsigned int size, unsigned int offset,
                                unsigned long flags, void (*ctor)(void *, kmem_cache_t *, unsigned long),
                                void (*dtor)(void *, kmem_cache_t *, unsigned long))
{
    char *func_nm = "KERNEL ERR kmem_create: ";
    unsigned int left_over, align, slab_size;
    kmem_cache_t *cachep = NULL;

    /*
     * 名称不能为空且长度合法（小于 CACHE_NAMELEN）
     * 不能在中断上下文中调用（可能睡眠）
     * 对象大小必须≥一个字，且不能过大
     * 偏移量必须在合理范围内
	 */
    if ((!name) ||
        ((strlen(name) >= CACHE_NAMELEN - 1)) ||
        in_interrupt() ||
        (size < BYTES_PER_WORD) ||
        (size > (1 << MAX_OBJ_ORDER) * PAGE_SIZE) ||
        (offset < 0 || offset > size))
        BUG();

    // 确保只使用允许的创建标志，防止非法组合。
    if (flags & ~CREATE_MASK)
        BUG();

    // 使用特殊的 cache_cache 来分配 kmem_cache_t 对象本身
    cachep = (kmem_cache_t *)kmem_cache_alloc(&cache_cache, SLAB_KERNEL);
    if (!cachep)
        goto opps;
    memset(cachep, 0, sizeof(kmem_cache_t));

    // 确保对象大小是字对齐的
    if (size & (BYTES_PER_WORD - 1))
    {
        size += (BYTES_PER_WORD - 1);
        size &= ~(BYTES_PER_WORD - 1);
        printk("%sForcing size word alignment - %s\n", func_nm, name);
    }

    align = BYTES_PER_WORD;
    if (flags & SLAB_HWCACHE_ALIGN)
        align = L1_CACHE_BYTES;

    // 决策逻辑：如果对象大小 ≥ 页面大小的1/8，使用 OFF-SLAB（管理结构外置）。
    if (size >= (PAGE_SIZE >> 3))
        flags |= CFLGS_OFF_SLAB;

    if (flags & SLAB_HWCACHE_ALIGN)
    {
        // 优化小对象：确保每个缓存行能存放至少2个对象
        while (size < align / 2)
            align /= 2;
        size = (size + align - 1) & (~(align - 1));
    }

    /*
     * 从最小的 gfporder（0，即1页）开始尝试
     * 调用 kmem_cache_estimate 计算该大小下能存放的对象数量和剩余空间
     * 
     * 检查各种终止条件：
     * 达到最大 order
     * OFF-SLAB 对象数超限（需要减小order）
     * 内部碎片可接受（剩余空间 <= slab总大小的1/8）
     * 达到预设的 break order
     */
    do
    {
        unsigned int break_flag = 0;
    cal_wastage:
        kmem_cache_estimate(cachep->gfporder, size, flags, &left_over, &cachep->num);
        if (break_flag)
            break;
        if (cachep->gfporder >= MAX_GFP_ORDER)
            break;
        if (!cachep->num)
            goto next;
        if (flags & CFLGS_OFF_SLAB && cachep->num > offslab_limit)
        {
            // 对于 OFF-SLAB，如果对象数超过限制，减小 order
            cachep->gfporder--;
            break_flag++;
            goto cal_wastage;
        }

        // 达到预设的 break order
        if (cachep->gfporder >= slab_break_gfp_order)
            break;

        // 检查内部碎片是否在可接受范围内（剩余空间 ≤ slab总大小的1/8）
        if ((left_over * 8) <= (PAGE_SIZE << cachep->gfporder))
            break; 
    next:
        cachep->gfporder++; // 增加 order 继续尝试
    } while (1);

    // 确保至少能存放一个对象
    if (!cachep->num)
    {
        printk("kmem_cache_create: couldn't create cache %s.\n", name);
        kmem_cache_free(&cache_cache, cachep);
        cachep = NULL;
        goto opps;
    }
    slab_size = L1_CACHE_ALIGN(cachep->num * sizeof(kmem_bufctl_t) + sizeof(slab_t));

    // 如果 OFF-SLAB 且剩余空间足够放 slab 管理结构，则关闭 OFF-SLAB
    if (flags & CFLGS_OFF_SLAB && left_over >= slab_size)
    {
        flags &= ~CFLGS_OFF_SLAB;
        left_over -= slab_size;
    }

    if (!cachep->gfporder && !(flags & CFLGS_OFF_SLAB))
        flags |= CFLGS_OPTIMIZE;

    // 初始化缓存结构
    cachep->flags = flags;
    cachep->gfpflags = 0;
    if (flags & SLAB_CACHE_DMA)
        cachep->gfpflags |= GFP_DMA;
    spin_lock_init(&cachep->spinlock);
    cachep->objsize = size;
    INIT_LIST_HEAD(&cachep->slabs);
    cachep->firstnotfull = &cachep->slabs;

    // 为 OFF-SLAB 的管理结构寻找合适的通用缓存
    if (flags & CFLGS_OFF_SLAB)
        cachep->slabp_cache = kmem_find_general_cachep(slab_size, 0);
    
    strcpy(cachep->name, name);

#ifdef CONFIG_SMP
    if (g_cpucache_up)
        enable_cpucache(cachep);
#endif
    {
        struct list_head *p;
        list_for_each(p, &cache_chain)
        {
            kmem_cache_t *pc = list_entry(p, kmem_cache_t, next);
            if (!strcmp(pc->name, name)) // 名称必须唯一
                BUG();
        }
    }

    list_add(&cachep->next, &cache_chain); // 加入全局链表
    //up(&cache_chain_sem);
opps:
    return cachep;
}

/*
 * 初始化用于管理 kmem_cache_t 结构的特殊缓存 cache_cache
 * 初始化通用大小缓存
 */
void kmem_cache_init(void)
{
    unsigned int left_over;

    cache_cache.slabs.next = &cache_cache.slabs;
    cache_cache.slabs.prev = &cache_cache.slabs;
    cache_cache.firstnotfull = &cache_cache.slabs;
    cache_cache.objsize = sizeof(kmem_cache_t);
    cache_cache.flags = SLAB_NO_REAP;
    memset(&cache_cache.spinlock, 0, sizeof (spinlock_t));
    strcpy(cache_cache.name, "kmem_cache");

    //init_MUTEX(&cache_chain_sem);
    INIT_LIST_HEAD(&cache_chain);

    kmem_cache_estimate(0, cache_cache.objsize, 0, &left_over, &cache_cache.num);

    if (!cache_cache.num)
        BUG();

    spin_lock_init(&cache_cache.spinlock);
}

/*
 * 初始化通用大小缓存
 * 这些缓存用于分配常见大小的对象，提升分配效率 
 */
void kmem_cache_sizes_init(void)
{
    cache_sizes_t *sizes = cache_sizes;
    char name[20];
    
    // 根据系统内存大小调整 slab_break_gfp_order
    if (num_physpages > (32 << 20) >> PAGE_SHIFT)
        slab_break_gfp_order = BREAK_GFP_ORDER_HI;
    
    do 
    {
        sprintk(name, "size-%d", sizes->cs_size);
        if (!(sizes->cs_cachep = kmem_cache_create(name, sizes->cs_size, 0, SLAB_HWCACHE_ALIGN, NULL, NULL)))
            BUG();

        // 对于 OFF-SLAB 缓存，计算允许的最大对象数(经验值)
        if (!(OFF_SLAB(sizes->cs_cachep)))
        {
            offslab_limit = sizes->cs_size - sizeof(slab_t);
            offslab_limit /= 2;
        }

        // 创建支持 DMA 的缓存
        sprintk(name, "size-%d(DMA)", sizes->cs_size);
        sizes->cs_dmacachep = kmem_cache_create(name, sizes->cs_size, 0, SLAB_CACHE_DMA | SLAB_HWCACHE_ALIGN, NULL, NULL);
        if (!sizes->cs_dmacachep)
            BUG();

        sizes++;
    } while (sizes->cs_size);
}

#if DEBUG
#define CHECK_NR(pg)                                           \
    do                                                         \
    {                                                          \
        if (!VALID_PAGE(pg))                                   \
        {                                                      \
            printk(KERN_ERR "kfree: out of range ptr %lxh.\n", \
                   (unsigned long)objp);                       \
            BUG();                                             \
        }                                                      \
    } while (0)
#define CHECK_PAGE(page)                              \
    do                                                \
    {                                                 \
        CHECK_NR(page);                               \
        if (!PageSlab(page))                          \
        {                                             \
            printk(KERN_ERR "kfree: bad ptr %lxh.\n", \
                   (unsigned long)objp);              \
            BUG();                                    \
        }                                             \
    } while (0)

#else
#define CHECK_PAGE(pg) \
    do                 \
    {                  \
    } while (0)
#endif

/*
 * 分配指定大小的内存
 * size：请求的内存大小
 * flags：分配标志，控制分配行为
 */
void *kmalloc(unsigned int size, unsigned int flags)
{
    void *p = NULL;
    cache_sizes_t *csizep = cache_sizes;

    for (; csizep->cs_size; csizep++)
    {
        if (size > csizep->cs_size)
            continue;
        p =  __kmem_cache_alloc(flags & GFP_DMA ? csizep->cs_dmacachep : csizep->cs_cachep, flags);
        if (p)
            memset(p, 0, size);
        return p;
    }
    
    BUG(); 
    return NULL;
}

void *kzalloc(unsigned int size, unsigned int flags)
{
    return kmalloc(size, flags);
}


/*
 * 释放由 kmalloc 分配的内存
 */
void kfree(void *objp)
{
    kmem_cache_t *c;
    unsigned long flags;

    if (!objp)
        return;
    local_irq_save(flags);
    CHECK_PAGE(virt_to_page(objp));
    c = GET_PAGE_CACHE(virt_to_page(objp));
    __kmem_cache_free(c, (void *)objp);
    local_irq_restore(flags);
}
